這裡需要先假設讀者已經大概知道一般c語言基礎語法,以下會概述我在學習中碰到的有趣新知識!
學習c的時候,許多人會對指標避之唯恐不及,但是在研究作業系統時,它卻是避不開的第一關。
如果還對指標不熟,可以先看看C語言: 超好懂的指標,初學者請進~
接下來,我們來看一個使用pointer to pointer的例子(來自jserv老師你所不知道的C語言:linked list 和非連續記憶體)
void remove_list_node(List *list, Node *target)
{
// The "indirect" pointer points to the *address*
// of the thing we'll update.
Node **indirect = &list->head;
// Walk the list, looking for the thing that
// points to the node we want to remove.
while (*indirect != target)
indirect = &(*indirect)->next;
*indirect = target->next;
}
我是這樣理解的:(先說明,pointer我都想成指向一個特定結構的地址,例 int *a; a = the address of an interger)
所以,list->head本身的值是一個node的地址,而&list_head就是此指標的地址。
而indirect中的值為此指標所儲存的值,即一個node的地址。
所以當indirect中node的地址不等於target所儲存node的地址,我們把indirect所儲存指標的位址換下一個指標位址!
如此一來,程式會變得更精簡,因為不須考慮移除node為第一個或最後一個的特殊案例!
這有沒有很神奇!我寫的可能還是太雜亂,建議自己畫圖理解一次會比較好!
在寫c語言,如果使用qsort等內建函式,如何理解最後一個參數(compare fucntion)?
以下截取自Linux Programmar's Manual
void qsort(void *base, size_t nmemb,
size_t size,int (*compar)(const void *, const void *));
其中,compar是一個函式,輸入值為兩個const void * ,而輸出為一個integer。
接著,我們再來看更複雜一點的例子!
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
我們先看,typedef中,我們定義了sighandler_t這一個函式,輸入為一個integer,輸出為void。
最後,我們了解signal這個函式,輸入有兩個,一個integer和一個sighandler_t,而輸出也是一個sighander_t的函式!
有沒有覺得有趣呢!我剛學到的時候,有重燃學程式的欲望
我們在編譯(compile)一個c程式(program),除了直接取得目的檔(object file),我們的gcc(是一個compiler driver之後會詳述)其實中間做了許多事呢!其中一個就是前置處理(preproccess),詳細敘述請參考jserv老師的你所不知道的 C 語言:前置處理器應用篇。
而我們的macro(巨集),就是在此一階段被展開,讓我們的程式更精簡且具更高擴充性!
注意!這裡我們必須知道還有一個叫gcc attribute的東西,跟macro是不一樣的喔!gcc attribute是給gcc編譯時所看的東西,以下先舉一個gcc attribute的小例子
struct __attribute__ ((__packed__)) my_node {
int val;
char name;
};
在這裡,attribute ((packed)) 是告訴gcc不要內存對齊!什麼是內存對齊(alignment)?就是讓儲存最小單位固定(一般預設長度為4bytes),以達到cpu儲存變數的速度加快(為什麼之後在cpu結構介紹會了解的!)。
在以上的例子中,若有packed,則sizeof(my_node) = 8,若無,則sizeof(my_node) = 5。
好了,我們回到macro,先講一個簡單的東西: do{}while(0)
以下為例子:
#define foo() do{\
func1();\
func2();\
}while(0)
int main(){
foo();
return 0;
}
在寫多個語句時,為了避免展開時造成混亂,我們必須先加上這個看似不起任何作用的do...while,注意,在while後面不要加上";",展開後為以下(使用gcc -E)
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "test.c"
int main(){
do{ func1(); func2(); }while(0);
return 0;
}
今天先寫到這,明天繼續講macro和其他c語言的議題~